﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;

namespace Escuela.UI
{
    public static class ConsoleHelper
    {
        public static void Header()
        {
            ILExpressions.__SetColor(ConsoleColor.Yellow, "set_ForegroundColor" );
            ILExpressions.__SetColor(ConsoleColor.Blue  , "set_BackgroundColor" );

            ILExpressions._WriteLine("     Programación Orientada a Objectos       ");
            ILExpressions._WriteLine("          Universidad de Celaya              ");
        }

        public static Dictionary<string, string> Ask(List<String> QuestionNames, string OutputFormatter)
        {
            Dictionary<string, string> result =
                new Dictionary<string, string>();

            // Do this for all the questions in the list.
            foreach (string item in QuestionNames)
            {
                UI.ConsoleHelper.WriteLine(OutputFormatter, "introducir>", item); //print the question
                string introducedData = ConsoleHelper.ReadLine("introducir"); //read data entered
                result.Add(item, introducedData); 
            }
            return result;
        }

        public static void WriteLine(this string str)
        {
            ILExpressions._WriteLine(str);
        }

        public static void WriteLine(this string message, string signature, params Object[] Params)
        {
            ConsoleColor PrevBG = Console.BackgroundColor;
            ConsoleColor PrevFG = Console.ForegroundColor;

            Console.ForegroundColor = ConsoleColor.Cyan; 
            Console.Write(signature);

            Console.ForegroundColor = PrevFG;

            Console.WriteLine(message, Params); 

        }

        public static string ReadLine(string signature)
        {
            ConsoleColor PrevBG = Console.BackgroundColor;
            ConsoleColor PrevFG = Console.ForegroundColor;

            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.Write(signature);

            Console.ForegroundColor = PrevFG;

            return Console.ReadLine();
        }
    }


    public class ILExpressions
    {
        private delegate void writeString(string str);
        private delegate string ReadString();
        private delegate void SetColor(ConsoleColor Color);

        public static void _WriteLine(string message)
        {
            Type[] _args = { typeof(string) }; /*The arguments to send to Console::WriteLine method 
                                                * Just one string arg will be used */

            /*One DynamicMethod to put IL on it */
            DynamicMethod WriteStr = new DynamicMethod("__WriteLine", typeof(void), _args);

            /*This MethodInformation will be used to call the WriteLine method that uses
             * one string arg, on the Type Console */
            MethodInfo WriteStrMethod = typeof(Console).GetMethod("WriteLine", _args);

            ILGenerator il = WriteStr.GetILGenerator(256); //Get the IL Generator

            //Start generating and emiting IL

            il.Emit     (OpCodes.Ldarg_0                    ); // Load Arg 0
            il.EmitCall (OpCodes.Call, WriteStrMethod, null ); // Call WriteLine Method
            il.Emit     (OpCodes.Ret                        ); // Return

            //End generating and emiting IL

            //Create a Delegate to execute the generated IL
            writeString invoker =
                (writeString)WriteStr.CreateDelegate(typeof(writeString));

            invoker.DynamicInvoke(message);//Execute the delegate, and the IL

        }

        public static string _ReadLine()
        {
            Type[] _args = { }; /*The arguments to use in the Console::ReadLine method, 
                                 * no any args at this method */

            // Create one method to put the IL on it
                        DynamicMethod ReadStr = 
                new DynamicMethod("__ReadLine", typeof(string), _args);

            /* This method information will be used to call
            the ReadLine method on the Console Type */
            MethodInfo ReadStrMethod = typeof(Console).GetMethod("ReadLine", _args);

            /*Generate an IL Generator */
            ILGenerator il = ReadStr.GetILGenerator(256);

            //Start genetating and emiting IL

            il.Emit     (OpCodes.Nop                       ); // No Operation
            il.EmitCall (OpCodes.Call, ReadStrMethod, null ); // Call method to ReadLine()
            il.Emit     (OpCodes.Ret                       ); // Return

            //End generating and emiting IL

            //Use a delegate to execute the generated IL
            ReadString invoker = (ReadString)ReadStr.CreateDelegate(typeof(ReadString));

            return (string)invoker.DynamicInvoke(null);//Execute and return the value
        }

        public static void __SetColor(ConsoleColor Color, string Target)
        {
            Type[] _args = { typeof(ConsoleColor) }; /* The arguments for the DynamicMethod, 
                                                      * We will use one ConsoleColor */

            
            /*Generate the DynamicMethod to put the CIL on it */
            DynamicMethod SetFGColor = new DynamicMethod("__SetFGColor", typeof(void), _args);

            /* This Method Information will be used to call the Set_ForegroundColor 
             * or Set_BacgroundColor, both takes one Arg of type ConsoleColor */
            MethodInfo SetFGColorMethod = typeof(Console).GetMethod(Target, _args);

            ILGenerator il = SetFGColor.GetILGenerator(256); //Create the IL Generator.

            //Start generating IL 

            il.Emit     (OpCodes.Nop                          ); //No Operation
            il.Emit     (OpCodes.Ldarg_0                      ); //Load Arg 0
            il.EmitCall (OpCodes.Call, SetFGColorMethod, null ); //Call method to set Foreground Color
            il.Emit     (OpCodes.Nop                          ); //No Operation
            il.Emit     (OpCodes.Ret                          ); //Return

            //End Generating IL

            SetColor invoker = 
                (SetColor)SetFGColor.CreateDelegate(typeof(SetColor)); //Create a Delegate to execute the generated IL

            invoker.DynamicInvoke(Color); //Execute IL with args
        }
    }
}
